Testing in ProductionとMonitoring Driven Developmentについて調べたまとめ
サーバーレス開発部の阿部です。
今日はサーバーレス開発のテストにつきまとう課題について調べていたらたどり着いた言葉であるTesting in ProductionとMonitoring Driven Developmentを取り上げてみようと思います。
【背景】サーバーレス開発のテストにつきまとう課題
以前、Serverless ArchitectureとMicroservice Architectureの違いに触れた時に、Serverless Architectureの特徴としてマネージドサービスを使い倒すことをあげました。 マネージドサービスは便利なのですが、使い倒すという観点になるとこれがなかなかに曲者です。以前サーバーレス開発部の和田もこのような記事を書いております。
マネージドサービスを積極的に利用する際に一番のハードルは、ローカル実行のための環境が作りづらいということです。これは、デバッグとテストのやり方において以下のようなアプローチを促進します。
- ユニットテスト可能な関数に切り分けてLambdaのコール部分はテストを書かない
- LocalStackなどを使った積極的なモッキング
これでとりあえずCI/CDを仕掛けられるようにはなります。 前者はロジックのシンプルさに繋がりテスタビリティをあげるという意味で歓迎すべきことです。後者の積極的なモッキングについては、仕方のないところですが是非の分かれるところですね。
これらモックで実現されるテスト環境は言ってみればフェイクの環境です。インフラリソースそのものをツールによってシミュレートすることになるため、テストの実施には常に「ツールの実装が利用するインフラリソースとは完全に一致しない」問題を孕んでいることになります。
Testing in ProductionとMonitoring Driven Development
この課題に対する一つのアプローチとして最近聴くようになった言葉がTesting in ProductionとMonitoring Driven Developmentです。前者、今までの開発の常識から考えると非常に物騒な言葉ですよね。正直なところ、私もTesting in Productionという言葉を初めて聴いた時に、ユーザにエラーを押し付けるのかと反発を覚えてしまいました。
どちらも、過激で、全てのコンテキストで有効ではないが、多くの場合で有効だったものとしてMartin Fowlerのブログで、サーバーレス開発における今後洗練が期待されるテスト手法として紹介されています。
【問題提起】ステージング環境≒イミテーション?
Testing in Productionはそもそもステージング環境もイミテーションではないのか?という問題提起を起点にしています。
統合テストや受入テスト向けにステージング環境を作るというアプローチは一般的ですが、このステージング環境とProductionには以下の点で違いがあることがほとんどです。
- サイジング
- データボリューム
- トラフィック量
これは費用の問題でもありますし、利用者自体がProductionと比べて多くないという問題もあります。
で、Testing in Productionを積極的に推進しようとしている人はこの状況に対して以下のような問題点があると感じています。
- サイズが違うということは大抵設定が違う、ということを意味する(クラスタ、データベース、ロードバランサ、キュー)
- データボリュームがProductionと違いすぎてテーブルスキャンがかかっていても検知できないケースがある
- ステージング環境でテストをすることで、確認バイアスを作ってしまう
- モニタリングしてないかあっても不正確
これらの問題点に対して、各所ブログを読んでいるとステージング環境なんてモックに過ぎないよねとか、俺のマシンでは動いたよ、のよりましなものでしかないとか、設定の変更はコードの変更よりも危険とか、ちょっと過激な物言いが並んでいました。
まあ、ここまで言ってしまうかはともかくとして、そういう意味では機能的な保証しかできないステージング環境よりもリアルトラフィックで安全にテストを実施したいという要求自体は理解できます。
Testing in Productionとは
テストは可能な限りProductionと状態を近づけて実施されるベストエフォートな検証である、という前提を発想の起点にして、シミュレーション環境ではなくプロダクション環境をテストに使おう、という発想でテスト環境やテストの実施を管理していくことです。今回は、私が読んだ中で一番議論が深かったTesting in Production, the safe wayというブログのポストを元に整理してみたいと思います。
とはいえ、Productionでテストをすると言ってもユーザ体験を損ないたい訳ではありません。ユーザに影響をなるべく出さないように安全に行う必要があります。Test in ProductionではあってもTesting by User(藤村さん、この表現いただきます)ではないということですね。
そのためにPre-ProductionでのテストとProductionでのテストを以下のフェーズに分けて考えています。
- Pre-Production
- Testing in Production
- Deploy
- Release
- Post-Release
Pre-Production
となるとTesting in ProductionとPre-Productionを分ける場合に、Pre-Production状態ですべきテストは何か、という話ですが、今まで通りCIで流していたもの全体と考えていいと思います。テストコード、テストケースなどで宣言的に表せるものが中心です。
Deploy
デプロイは新しい環境が本番環境へのサーバなどの設置が済んでいて、トラフィックを振り分けることが可能な状態をさしています(Deploy!=Relase)。あくまで状態のみで、この段階ではユーザに公開はしません。
この状態を維持したまま、以下のようなテストを行います。
- Integration Test
- Shadowing
- Tap compare
- Load Test
- Config Test
このうち、ShadowingとTap Compareは新しいシステムにリアルトラフィックからサンプリングしたリクエストを飛ばしてテストための方法論です。
Shadowingはリアルトラフィックからサンプリングするものをコピーして新しいシステムに飛ばします。この時、リアルトラフィックを受けた旧システムがどのような値を返しているかは関知せず、新しいサービスの結果のみを検証の対象とします。
Tap Compareはリアルトラフィックからサンプリングするところまでは同じですが、旧システムがリアルトラフィックを受けてどのように返したかを関知して新旧比較を行います。
これらを実現することで実際のトラフィック(エッジケースも含めて)からテストをすることができるわけですが、Productionに最大で2倍のトラフィックが流れることになりますので、それだけのトラフィックを捌ききるための準備が必要になります。 サンプリングするトラフィックに依存関係が発生するとテストの構築がしづらいため、ShadowingやTap Compareを実施するのはステートレスで独立性の高いリクエスト向けのテストに採用することを推奨しています。
ステートフルだったりインタラクションが必要なものについてはIntegration Testでの実施を推奨していますが、このIntegration Testの概念がちょっと今までと異なります。 サービスディスカバリーなどを利用して、Production環境内にリアルトラフィックから切り離された小さなユニットを作り、その中でContract Testなどを行うやり方をIntegration Testと想定しているようです。
上記のようなテスト環境を実現するためには、いくつかの注意事項があります。
- デプロイされた新サービスがわかるようにタグづけされていること
- Productionを使ってテストするため、永続化層やキャッシュなどの汚染に注意する
- CREATEなどは永続化層に届く前に捨てる仕組みにするかフラグを事前に設計する
注意事項を解決するためにProductionのアーキテクチャを整えていく、というのも活動の一つのようです。進化的アーキテクチャはここに繋がる話なのだと理解しました。
Release
リリースはユーザからのリアルなトラフィックを段階的に新しいバージョンに振り向けながらテストを実施していく段階で、以下のような活動を中心としています。
- カナリアリリース
- メトリクスの取得
- エラーレートの増加
- 要求レートの低下や停止
- レイテンシの増加
- Exception Tracking
- Traffic Shifting
このあたりまでいくと活動の傾向としてはわかりやすいです。 デプロイまでの段階でProductionのノードに載せるための基本的な検証ができているので、Productionのリアルトラフィックの一部を担わせながら新しいバージョンの稼働状態の検証を行っていくことが目的です。
Traffic Shiftingは計測結果などを見ながら新バージョンへのリアルトラフィックの割合を増やしていくことで、実際にはサービスメッシュなどを使って実施していることが多いようです。
このフェーズで重要なのは、メトリクスやExeption Trackingのトリアージ結果に伴って環境のロールバックとロールフォワードがすぐに可能であることです。
Post-Release
この段階まで来ると、リアルトラフィックはほぼ切り替わっているはずなので、必要なデバッグに繋げられるような以上を検知するための活動が中心です。
- 機能のフラグ制御
- A/Bテスト(ユーザ体験のメトリクスとしてよりもチューニング観点で実施)
- Logs/Events, Metrics and Tracing
- 環境のプロファイリング
- Teeing
- Chaos Engineering
A/Bテストをチューニング観点で実施する、というのはすごく新鮮でした。
Teeingはデバッグ目的でトラフィックを再現するために、リアルトラフィックをteeコマンドのようにストレージなどにプールすることです。AWSのマネージドサービスという観点ではCloudTrailがあるので、これを中心に考えればいいのかな、と思います。
Chaos Engineeringは分散システムに対して、意図的に外乱を引き起こして予測どおりの挙動をするかどうかの実験です。ツールとして有名なのはNetflixのChaos Monkeyですね。
Monitoring Driven Development(MDD)
さて、もう一つの話題、Monitoring Driven DevelopmentはTDDのようなサイクルをビジネスフェーズのために実施することを目的とした開発サイクルの考え方です。Productionへのモニタリングを通じて行うため、Testing in ProductionのReleaseやPost-Productionフェーズで行う活動の中に取り込まれそうな感じを受けます。
モニタリングする値をビジネスフェーズの目標に沿って、事前に考えておきましょう(この部分をさしてDrivenと呼称しているのだと思われます)、という考え方です。一部具体的なプロセスについて触れているブログはありますが、あまり整理されていないように感じました。
ただ、正直なところまだ議論がそこまで進んでない印象です。気になるのはフィードバックが比較的ロングスパンであることで、TDDでRedに相当する箇所が非常に長くなるのではないかと懸念されるのですが、その点に具体的に触れている記事は観測範囲では見当たりませんでした。
まとめ
今日はTesting in ProductionとMonitoring Driven Developmentについて整理をしてみました。両者をそれぞれまとめると以下のようになります。
- Testing in Production: Production環境を使って段階的にユーザのリアルトラフィックに晒して検証すること
- Monotoring Driven Development: ビジネス要求で検証したいモニタリング要素から開発を駆動するプロセス
Testing in Productionの方が議論が具体的に進んでいる印象です。どのようなアプローチで取り組んでいって、どのような環境が要求されるのか、議論の内容がわかりやすかったです。個人的にはMonitoring Driven Developmentはこれからの議論を待つ必要はありますが、Testing in Productionのいずれかのフェーズの取り組み方として集約されていきそうな印象を持ちました。
アプリケーションのログやイベント収集にしても、マネージドサービスを中心にしておけば特に気にすることなくCloudWatchに集約可能ですし、監視についてはCloudWatch、利用のトラッキングについてはCloudTrailがあるので、ReleaseからPost-ReleaseについてはAWSのマネージドサービス中心に構成できそうな印象があります。 環境のバージョニングやタグ付けについてもAPI GatewayやLambdaなどで実現されているため、どこまで自動化するのか、という観点で考えればLambdaを通じてコントロールできそうな感触があります。
Deployフェーズについては、今回の調べごとを通じて具体的なツールやアプローチが見えてきたので、Lambdaやマネージドサービスを使う際のTesting in Productionの構成について検証してみたいと思いました。できればこの検証環境もサーバーレスで。
【参考】Testing in Productionを支えるツール群
- Pact: Contract Testingを行うためのツール
- NDBench: Netflixが作っているデータソースのためのベンチマークツールで、クラウドのデータストアにも対応している
- Diffy: リクエストをマルチキャストして比較するためのツール
- scientist: 新旧比較でリファクタリングを支援するためのツール(他言語実装あり)
- FlameGraphs: Stack Trace Visualizer(環境のプロファイリングを行う)
- Spinnaker: Continuous Delivery Tool(カナリアリリースのサポートが可能)
- Sentry: エラーレポートにフォーカスしたクロスプラットフォームなアプリケーションモニタリングツール
【参考】ブログやセッション
Testing in Production
-
Testing in Production, the safe way 今回一番読み応えがあったブログです。筆者の考えだけではなく、カンファレンスで様々なエンジニアと議論をした結果を取り入れているため、具体的なレベルでのフェーズわけなどが進んでいて参考になります。
-
Testing in Production - The Good, The Bad and the Less Pretty 海外のカンファレンスでのTesting in Productionのセッションです。
Monitoring Driven Development
-
Monitoring Driven Development: Are you making money?
- re:Invent 2017でのセッション モニタリングを効果的に行うための条件の話に終始していた印象。